Skip to content

使用 .NET 產生帶有浮水印的 Excel

TLDR

  • Excel 無內建浮水印功能,可透過設定「滿版背景圖片」或「頁首圖片」來模擬。
  • 產生浮水印圖片時,需根據 Excel 的 PaperSize 設定調整圖片尺寸,並注意橫向列印時需對調寬高。
  • EPPlus 可透過 sheet.BackgroundImage.Image 設定背景,但需注意 .NET 6+ 對 System.Drawing.Common 的支援限制。
  • NPOI 無直接 API,需透過底層 POIXMLDocumentPart 自定義 VmlDrawing 並手動操作 XML 結構來掛載圖片。
  • 建議使用 SkiaSharp 取代 System.Drawing.Common 以解決跨平台相容性問題。

產生滿版浮水印圖片的原理

Excel 的浮水印效果取決於檢視模式:

  • 標準模式與整頁模式:可透過設定 Background Image 顯示。
  • 整頁模式與列印:可透過設定 Header Image 顯示。

在產生圖片前,必須先取得目標紙張大小(PaperSize)。以下為常見紙張的像素參考(以預設解析度為準):

PaperNameWidthHeight
A48271169
A311691654
Letter8501100

調整圖片尺寸

若圖片尺寸不符合紙張,需進行縮放並置中處理。若 PaperSize 為橫向,請務必將 widthheight 參數對調。

csharp
public Image ResizeImageBackgroundToFullPage(Image watermark, int width, int height) {
    if (watermark.Width > width || watermark.Height > height) {
        using (Image image = ZoomOutImage(width, height)) {
            return ResizeImageBackgroundToFullPageInternal(width, height, image);
        }
    }
    return ResizeImageBackgroundToFullPageInternal(width, height, watermark);
}

使用 EPPlus 產生浮水印

什麼情況下會遇到這個問題:當開發環境使用 EPPlus 且專案目標為 .NET Framework 或舊版 .NET 時。

在 EPPlus 中,可以直接透過 HeaderFooterBackgroundImage 屬性設定:

csharp
sheet.HeaderFooter.OddHeader.InsertPicture(watermark, PictureAlignment.Centered);
sheet.BackgroundImage.Image = watermark;

WARNING

EPPlus 6 以後的版本移除了對 System.Drawing.Common 的依賴,若升級至新版,需改用其他繪圖函式庫處理圖片物件。

使用 NPOI 產生浮水印 (XLSX)

什麼情況下會遇到這個問題:當專案無法使用 EPPlus,或需要更底層的控制權時。

NPOI 沒有直接設定背景圖的 API,必須手動建立 VmlDrawing 並處理 XML 關聯。

1. 定義 VmlDrawing 類別

需繼承 POIXMLDocumentPart 並實作 Commit 方法,將圖片資訊寫入 VML 結構中:

csharp
private class VmlDrawing : POIXMLDocumentPart {
    public string PictureRelId { get; set; }
    public Image Image { get; set; }

    protected override void Commit() {
        PackagePart part = GetPackagePart();
        using Stream @out = part.GetOutputStream();
        // 將 Pixel 轉換為 Points 單位
        float width = Image.Width * 72 / Image.HorizontalResolution;
        float height = Image.Height * 72 / Image.VerticalResolution;

        // 寫入 VML XML 結構
        // 注意:此處需根據實際需求填入正確的 XML 節點與屬性
    }
}

2. 設定浮水印關聯

透過 AddRelation 將圖片與 Sheet 進行關聯,並將 legacyDrawingHF 指向自定義的 VmlDrawing

csharp
// 將圖片加入 Workbook
int pictureIdx = workbook.AddPicture(imageMs.ToArray(), PictureType.PNG);
POIXMLDocumentPart docPart = workbook.GetAllPictures()[pictureIdx] as POIXMLDocumentPart;

// 建立 VML 關聯
VmlDrawing drawing = (VmlDrawing)sheet.CreateRelationship(VmlRelation.Instance, XSSFFactory.GetInstance(), drawingNumber);
drawing.Image = watermark;
drawing.PictureRelId = headerRelPart.Relationship.Id;

// 設定頁首圖片
sheet.Header.Center = HeaderFooter.PICTURE_FIELD.sequence;
sheet.GetCTWorksheet().legacyDrawingHF = new CT_LegacyDrawing {
    id = sheet.GetRelationId(drawing)
};

範例專案

更多實作細節與 .NET 10 環境下的調整(改用 SkiaSharp),請參考:CloudyWing/ExcelWatermarkSample


異動歷程

    • 初版文件建立。
    • 補上 GitHub 範例專案連結。